/*Yipeng Huang and Scott Rogowski*/
/*yh2315 smr2167*/

//The stage which handles the external data memory and stores data in delay registers
module MemoryStageWithRegisters (
/*AUTOARG*/
   // Outputs
   data_memory_value, ALU_result, data_o_valid, data_o_rw,
   data_o_addr, data_o_value, concurrent_data_read_write_error,
   data_delay_exception, program_counter_source, register_write,
   mem_to_register, register_write_address,
   // Inputs
   clk, reset, pause, ALU_result_input, data_memory_data_input,
   memory_read_input, memory_write_input, data_i_valid, data_i,
   branch_input, ALU_zero, register_write_input,
   mem_to_register_input, register_write_address_input
   );

	// basic inputs
	input clk, reset, pause;




	///////////////////////////////////////////////////////////
	///////////////////*DATA MEMORY LOGIC*/////////////////////
	///////////////////////////////////////////////////////////

	// inputs passed from the execute stage
	input [31:0] ALU_result_input;			//Coming from the ALU
	input [31:0] data_memory_data_input;	//Coming from register_1
	
	// memory stage control lines
	input memory_read_input;	//Whether to read from memory
	input memory_write_input;	//Whther or not to write to memory
	
	// data outputs buffered by register file
	output [31:0] data_memory_value;
	output [31:0] ALU_result;
		
	// data outputs to the external data memory
	// aka data memory requests
	output data_o_valid; // TODO: analyze
	output data_o_rw; // TODO: analyze
	output [31:0] data_o_addr;
	output [31:0] data_o_value;
	
	// data inputs from the external Data Memory 
	// aka, data memory responses
	input data_i_valid;
	input [31:0] data_i;
	
	// determine data memory control logic
	// deady to read/write to the data memory if either control lines 'memory write' or 'memory read' are enabled
	// output an error if both memory_write_input and memory_read_input are true
	assign data_o_valid = memory_write_input | memory_read_input; //TODO: analyze
	assign data_o_rw = memory_write_input & !memory_read_input;
	output concurrent_data_read_write_error;
	assign concurrent_data_read_write_error = memory_write_input & memory_read_input;
	
	// determine data memory address and value
	assign data_o_addr = ALU_result_input;			//The data memory address is the ALU result
	assign data_o_value = data_memory_data_input;	//The data memory value is passed from register_1
	
	// link data memory returned value to the register, and output it
	DelayRegisterFile data_memory_value_file (.clk(clk), .reset(reset), .pause(pause), .data(data_i), .q(data_memory_value));
	DelayRegisterFile ALU_result_file (.clk(clk), .reset(reset), .pause(pause), .data(ALU_result_input), .q(ALU_result));
	
	
	///////////////////////////////////////////////////////////
	/////////////////////*HAZARD LOGIC*////////////////////////
	///////////////////////////////////////////////////////////
	output data_delay_exception;
	FF data_delay_exception_ff (.clk(clk), .reset(reset | data_i_valid), .enable(data_o_valid | data_i_valid), .set(data_o_valid), .q(data_delay_exception));
	
	
	
	///////////////////////////////////////////////////////////
	/////////////////////*BRANCH LOGIC*////////////////////////
	///////////////////////////////////////////////////////////
	
	input branch_input;
	input ALU_zero;
	
	// control outputs not buffered
	output program_counter_source;
	
	// determine program counter source
	assign program_counter_source = ALU_zero & branch_input;
	


	///////////////////////////////////////////////////////////
	////////////*PASS-THROUGH INPUTS AND OUTPUTS*//////////////
	///////////////////////////////////////////////////////////
	
	// pass-through inputs
	input register_write_input;
	input mem_to_register_input;
	input [4:0] register_write_address_input;
	
	// pass-through outputs
	output register_write;
	output mem_to_register;
	output [4:0] register_write_address;
		
	// pass-through
	FF register_write_ff (.clk(clk), .reset(reset), .enable(!pause), .set(register_write_input), .q(register_write));
	FF mem_to_register_ff (.clk(clk), .reset(reset), .enable(!pause), .set(mem_to_register_input), .q(mem_to_register));	
	DelayRegisterFile #(5) register_write_address_file (.clk(clk), .reset(reset), .pause(pause), .data(register_write_address_input), .q(register_write_address));
	
	
	endmodule